home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 July: Mac OS SDK / Dev.CD Jul 96 SDK / Dev.CD Jul 96 SDK2.toast / Development Kits (Disc 2) / QuickDraw GX / Programming Stuff / Sample Code / Printing Samples / Extensions… / Backwash ƒ / Backwash.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-09-27  |  33.8 KB  |  1,143 lines  |  [TEXT/MPS ]

  1. /*________________________________________________________
  2.  
  3.     File: Backwash.c
  4.     
  5.     C source for a printing extension that adds a
  6.     QuickDraw picture to each page despooled.
  7.  
  8.     Dave Hersey
  9.     Apple Developer Technical Support
  10.  
  11.     11/11/92 - dmh - Created.
  12.      1/28/93 - dmh - Cleaned up for a5 seed CD.
  13.      3/26/93 - dmh - Updated for b1c2 (Translator changes).
  14.      8/24/93 - dmh - Updated for b2.
  15.               - The picture is now rolled into the spool
  16.                file as a resource, and applied to each
  17.                page during despooling.
  18.              - Added a TextEdit compatibility mode which
  19.                strips out the white rectangles drawn by
  20.                apps which use TextEdit to print (like
  21.                TeachText).
  22.              - The "intensity" panel item actually
  23.                functions now.
  24.              - Switched to Exception.h assertion stuff
  25.                for error checking.
  26.      9/09/93 - dmh - AddBackwash now displays progress info.
  27.     12/18/93 - dmh - Updated for b3.
  28.      3/22/94 - dmh - Updated for b4.
  29.      5/03/94 - dmh - Updated for f2.
  30.               - BWDespoolPage can be passed a nil format/shape.
  31.               - The flattened shape resource is no longer purgeable.
  32.                It wasn't making it back from GXDespoolResource
  33.                without getting purged.
  34.  
  35.     (Note: all functions are in the Mark menu.)
  36.  
  37. __________________________________________________________*/
  38.  
  39. #include "Backwash.h"
  40.  
  41.  
  42. /*******************************************************************
  43.     __Startup__ contains our jump table to the overrides.
  44.     
  45. ********************************************************************/
  46. #if defined(__MWERKS__)
  47. asm void __Startup__(void)
  48. {
  49.     DC.L        0                // GX needs this
  50.  
  51.     JMP        BWInitialize        // (offset =  4)
  52.     JMP        BWShutDown            // (offset =  8)
  53.     JMP        BWJobPrintDialog    // (offset = 12)
  54.     JMP        BWHandlePanelEvent    // (offset = 16)
  55.     JMP        BWCreateSpoolFile    // (offset = 20)
  56.     JMP        BWDespoolPage        // (offset = 24)
  57.     JMP        BWCloseSpoolFile    // (offset = 28)
  58.  
  59.     RTS                            // this is needed so __Startup__ symbol works
  60. }
  61. #endif
  62.  
  63.  
  64. /*******************************************************************
  65.     MyInitDataHandle is used to initialize any global data we need
  66.     from our GXInitialize message override.  .
  67.  
  68. ********************************************************************/
  69.  
  70. void MyInitDataHandle( MyDataHdl dataHandle )
  71. {
  72.     (**dataHandle).backwashShape = nil;
  73. }
  74.  
  75.  
  76. /*******************************************************************
  77.     GetBackwashShape returns the backwash shape from the structure
  78.     whose handle is stored ing the MessageHandler InstanceContext.
  79.  
  80. ********************************************************************/
  81. gxShape GetBackwashShape ( void )
  82. {
  83.     MyDataHdl dataHandle;
  84.     dataHandle = (MyDataHdl) GetMessageHandlerInstanceContext();
  85.     return (**dataHandle).backwashShape;
  86. }
  87.  
  88.  
  89. /*******************************************************************
  90.     BWInitialize is our override for the GXInitialize message.
  91.     In here, you shouldn't initialize anything directly-- call
  92.     InitGlobalData for that.  Once you create the A5 world with
  93.     NewMessageGlobals, you can access your global data just like
  94.     you were an app.  Whenever you're called, your global data will
  95.     be valid.
  96.     
  97. ********************************************************************/
  98.  
  99. OSErr BWInitialize()
  100. {
  101.     OSErr        err;
  102.     MyDataHdl    dataHandle;
  103.     
  104.     /*
  105.     Create a new temporary memory handle, initialize
  106.     it, and store it as the message handler's instance
  107.     context.
  108.     */
  109.     
  110.     dataHandle = (MyDataHdl) TempNewHandle(sizeof(MyDataRec),&err);
  111.     
  112.     if (err == noErr)
  113.     {
  114.         MyInitDataHandle(dataHandle);
  115.         SetMessageHandlerInstanceContext(dataHandle);
  116.     }
  117.     
  118.     return err;
  119. }
  120.  
  121.  
  122. /*******************************************************************
  123.     BWShutDown is our override for the GXShutDown message.  We
  124.     simply throw away our A5 world.
  125.     
  126. ********************************************************************/
  127.  
  128. OSErr BWShutDown()
  129. {
  130.     MyDataHdl dataHandle;
  131.     
  132.     /*
  133.     Retrieve the message handler's instance context. If the
  134.     value returned isn't nil, it's a handle that we stored
  135.     earlier. Dispose of the handle and set the instance
  136.     context to nil to "clear" it.
  137.     */
  138.     
  139.     dataHandle = (MyDataHdl) GetMessageHandlerInstanceContext();
  140.     
  141.     if (dataHandle != nil)
  142.     {
  143.         DisposeHandle((Handle) dataHandle);
  144.         SetMessageHandlerInstanceContext(nil);
  145.     }
  146.     
  147.     return noErr;
  148. }
  149.  
  150.  
  151. /*******************************************************************
  152.     BWJobPrintDialog is our override for GXJobPrintDialog.  All we
  153.     do is set up our panel and then forward the message.
  154.     
  155. ********************************************************************/
  156.  
  157. OSErr BWJobPrintDialog(gxDialogResult *dlogResult)
  158. {
  159.     OSErr    err;
  160.     
  161.     err = SetUpPrintPanel();
  162.  
  163.     if (!err)
  164.         err = Forward_GXJobPrintDialog(dlogResult);
  165.     
  166.     return err;
  167. }
  168.  
  169.  
  170. /*******************************************************************
  171.     BWHandlePanelEvent is our override for GXHandlePanelEvent.
  172.     If the event is one of ours, we handle it.  Otherwise, we
  173.     just forward it down the chain.
  174.     
  175. ********************************************************************/
  176.  
  177. OSErr BWHandlePanelEvent(gxPanelInfoRecord *panelInfo)
  178. {
  179.     OSErr                        err = noErr;
  180.     GrafPtr                        oldPort;
  181.     DialogPtr                    pDlg;
  182.     StandardFileReply            reply;
  183.     SFTypeList                    typeList;
  184.     BackwashCollection            backwashConfig;
  185.     Handle                        hItem;
  186.     Rect                        itemRect;
  187.     short                        itemType, oldResFile;
  188.  
  189. // Make sure that our resource file is at the top of the chain.
  190.  
  191.     oldResFile = CurResFile();
  192.     UseResFile(GXGetMessageHandlerResFile());
  193.  
  194.  
  195. // Set our current QuickDraw port to the dialog.
  196.  
  197.     pDlg = panelInfo->pDlg;
  198.     GetPort(&oldPort);
  199.     SetPort(pDlg);
  200.     
  201.     switch (panelInfo->panelEvt)
  202.     {
  203.  
  204. // If our panel is opening, go do any initialization we need to.
  205.  
  206.         case gxPanelOpenEvt:
  207.             OpenBackwashPanel(pDlg, panelInfo->itemCount);
  208.             break;
  209.  
  210.  
  211. // If the user hits the "Select Picture" button, prompt them for a file to load.
  212. // If they select one, get our old collection item, move the FSSpec info to it,
  213. // and replace the old collection item with our modified one.
  214.  
  215.         case gxPanelHitEvt:
  216.             if (panelInfo->itemHit == (panelInfo->itemCount + d_SelectPicture))
  217.             {
  218.  
  219. // Have the user select the PICT file to load.
  220.  
  221.                 typeList[0] = 'PICT';
  222.                 StandardGetFile(nil, 1, typeList, &reply);
  223.                 require(reply.sfGood, UserCancelledFileDialog);
  224.  
  225.  
  226. // Get the old "Backwash settings" collection item, and update the file reference.
  227.  
  228.                 err = GetJobCollectionItem(&backwashConfig,
  229.                                            nil,
  230.                                            kBackwashCollectionType,
  231.                                            kBackwashSettingsID);
  232.  
  233.                 nrequire(err, GetSettings_Failed);
  234.                 
  235.                 backwashConfig.haveFileInfo = true;
  236.                 BlockMove(&reply.sfFile, &backwashConfig.fileInfo, sizeof(FSSpec));
  237.  
  238.  
  239. // Replace the old collection item with the updated one.
  240.  
  241.                 err = AddCollectionItem(GXGetJobCollection(GXGetJob()),
  242.                                         kBackwashCollectionType,
  243.                                         kBackwashSettingsID,
  244.                                         sizeof(BackwashCollection),
  245.                                         &backwashConfig);
  246.  
  247.                 nrequire(err, StoreSettings_Failed);
  248.  
  249.  
  250. // Update the file name in our dialog.
  251.  
  252.                 GetDItem(panelInfo->pDlg, panelInfo->itemCount +d_FileNameItem,
  253.                          &itemType, &hItem, &itemRect);
  254.  
  255.                 SetIText(hItem, backwashConfig.fileInfo.name);
  256.             }
  257.             break;
  258.  
  259.  
  260. // If our panel is activating/deactivating or if the focus (which
  261. // section of the dialog is active) has changed we need to activate
  262. // our editText field appropriately.
  263.  
  264.         case gxPanelActivateEvt:
  265.         case gxPanelDeactivateEvt:
  266.         case gxPanelIconFocusEvt:
  267.         case gxPanelPanelFocusEvt:
  268.             if ((((DialogPeek) pDlg)->editField +1) == (panelInfo->itemCount +d_Intensity))
  269.             {
  270.                 if (panelInfo->panelEvt == gxPanelPanelFocusEvt)
  271.                     TEActivate(((DialogPeek) pDlg)->textH);
  272.                 else
  273.                     TEDeactivate(((DialogPeek) pDlg)->textH);
  274.             }
  275.             break;
  276.     }
  277.  
  278. // Jump points for misc. error conditions follow.
  279.  
  280. UserCancelledFileDialog:
  281. GetSettings_Failed:
  282. StoreSettings_Failed:
  283.  
  284.  
  285. // Finally, restore the original QuickDraw GrafPort and return.
  286.  
  287.     SetPort(oldPort);
  288.     UseResFile(oldResFile);
  289.     return err;
  290. }
  291.  
  292.  
  293. /*******************************************************************
  294.     BWCreateSpoolFile is our override for GXCreateSpoolFile.
  295.     It just gets our user settings collection item, sees if we're
  296.     supposed to do anything, and if so, adds a gxShape version of
  297.     the selected PICT to the spool file.
  298.  
  299. ********************************************************************/
  300.  
  301. OSErr BWCreateSpoolFile(FSSpecPtr anFSSpec, long createOptions, gxSpoolFile *theSpoolFile)
  302. {
  303.     OSErr                err;
  304.     BackwashCollection    backwashConfig;
  305.  
  306. // Forward the message so that the spool file is created for us.
  307.  
  308.     err = Forward_GXCreateSpoolFile(anFSSpec, createOptions, theSpoolFile);
  309.     nrequire(err, ForwardMessage_Failed);
  310.  
  311. // Get our collection item and see if we're enabled and have file info.  If
  312. // so, add the backwash picture to the spool file.  Note that if we don't
  313. // find our collection item we don't try to add the backwash.  The user may
  314. // not be printing with dialogs, so you have to deal with this situation…
  315. // Also, if we get an error when we try to add the Backwash shape, we
  316. // ignore the error but turn ourself off by removing our settings collection
  317. // item.  This way, printing can continue, and BWDespoolPage won't be
  318. // confused when there's no Backwash shape in the print file.
  319.  
  320.     err = GetJobCollectionItem(&backwashConfig,
  321.                                nil,
  322.                                kBackwashCollectionType,
  323.                                kBackwashSettingsID);
  324.                            
  325.     if (!err && backwashConfig.addBackwash && backwashConfig.haveFileInfo)
  326.     {
  327.         err = (OSErr) AddBackwash(&backwashConfig.fileInfo, (short) backwashConfig.intensity, *theSpoolFile);
  328.         require(err, BackwashAdded);
  329.         
  330.         RemoveCollectionItem(GXGetJobCollection(GXGetJob()),
  331.                              kBackwashCollectionType,
  332.                              kBackwashSettingsID);
  333.  
  334.         err = noErr;
  335.     }
  336.     else
  337.         if (err == collectionItemNotFoundErr)
  338.             err = noErr;
  339.  
  340. BackwashAdded:
  341. ForwardMessage_Failed:
  342.  
  343.     return err;
  344. }
  345.  
  346.  
  347. /*******************************************************************
  348.     BWDespoolPage is our override for the GXDespoolPage message.
  349.     We check to see if the user is adding a backwash, and if so,
  350.     retrieve the flattened shape from the spool file and apply it
  351.     to each page.  For efficiency, we only retrieve the shape once,
  352.     and then reference it via our instance-global, iBackwashShape.  That
  353.     shape is latered disposed of in BWCloseSpoolFile.
  354.     
  355. ********************************************************************/
  356.  
  357. OSErr BWDespoolPage(gxSpoolFile theSpoolFile, long thePageNum,
  358.                     gxFormat theFormat, gxShape *thePage,
  359.                     Boolean *formatChanged)
  360. {
  361.     OSErr                anOSErr;
  362.     gxGraphicsError        grErr;
  363.     Handle                vpListHdl, gxShapeHdl = nil;
  364.     gxRectangle            pBounds;
  365.     Fixed                cx, cy, px, py;
  366.     long                numViewPorts;
  367.     BackwashCollection    backwashConfig;
  368.     gxShape                iBackwashShape ;
  369.  
  370.     iBackwashShape = GetBackwashShape() ;
  371.     
  372. // Forward the message so that we get the page shape, then retrieve our
  373. // "Backwash settings" collection item.
  374.  
  375.     anOSErr = Forward_GXDespoolPage(theSpoolFile, thePageNum, theFormat, thePage, formatChanged);
  376.     nrequire((grErr = (gxGraphicsError) anOSErr), ForwardMessage_Failed);
  377.     require((theFormat != nil) && (thePage != nil), NotAddingBackwash);
  378.  
  379.     grErr = GetJobCollectionItem(&backwashConfig,
  380.                                  nil,
  381.                                  kBackwashCollectionType,
  382.                                  kBackwashSettingsID);
  383.  
  384.     require_action((!grErr && backwashConfig.addBackwash && backwashConfig.haveFileInfo),
  385.                     NotAddingBackwash, grErr = noErr;);
  386.  
  387.  
  388. // If we're in "TextEdit Compatibility" mode, remove white rectangles from the
  389. // page shape.
  390.  
  391.     if (backwashConfig.useTextEditMode)
  392.         RemoveWhiteRects(*thePage);
  393.  
  394.  
  395. // If we need to load the flattened shape resource, do so.  Note that we only
  396. // need to do this on the first page.  Thereafter, the shape reference is in
  397. // our instance-global.
  398.  
  399.     if (iBackwashShape == nil)
  400.     {
  401.         anOSErr = Send_GXDespoolResource(theSpoolFile, kBackwashCollectionType, r_BackwashPICTID, &gxShapeHdl);
  402.         nrequire((grErr = (gxGraphicsError) anOSErr), DespoolResource_Failed);
  403.     }
  404.  
  405.  
  406. // If we haven't already, unflatten the Backwash shape.  We need a
  407. // viewport list to unflatten the shape, so we get the current page's
  408. // viewport list and use that.
  409.  
  410.     if (iBackwashShape == nil)
  411.     {
  412.  
  413.         numViewPorts = GXGetShapeViewPorts(*thePage, nil);
  414.  
  415.         vpListHdl = TempNewHandle(numViewPorts * sizeof(gxViewPort), &anOSErr);
  416.         grErr = (gxGraphicsError) anOSErr;
  417.         nrequire_action(grErr, TempNewHandle_Failed, ReleaseResource(gxShapeHdl););
  418.     
  419.         HLock(vpListHdl);
  420.         GXGetShapeViewPorts(*thePage, (gxViewPort *) *vpListHdl);
  421.         iBackwashShape = HandleToShape(gxShapeHdl, numViewPorts, (gxViewPort *) *vpListHdl);
  422.         DisposHandle(vpListHdl);            // all done with this.
  423.  
  424.         if (gxShapeHdl)
  425.             ReleaseResource(gxShapeHdl);    // all done with this-- remember, it's a resource!
  426.  
  427.         nrequire(GXGetGraphicsError(&grErr), CouldNotSetUpShape);
  428.     }
  429.  
  430.  
  431. // Now we get the page dimensions from the current page format.  We use this
  432. // and the shape's bounds to center the picture on the page.  Once we've done
  433. // that, we add the GX picture shape behind all the other shapes in our page
  434. // shape and return.
  435.  
  436.     GXGetFormatDimensions(theFormat, &pBounds, nil);
  437.     grErr = GXGetJobError(GXGetJob());
  438.     nrequire(grErr, GetFormatDims_Failed);
  439.  
  440.     cx = pBounds.left + (pBounds.right - pBounds.left) >>1;
  441.     cy = pBounds.top + (pBounds.bottom - pBounds.top) >>1;
  442.  
  443.     GXGetShapeBounds(iBackwashShape, 0, &pBounds);
  444.     px = pBounds.left + (pBounds.right - pBounds.left) >>1;
  445.     py = pBounds.top + (pBounds.bottom - pBounds.top) >>1;
  446.     GXMoveShapeTo(iBackwashShape, cx-px, cy-py);
  447.  
  448.     GXSetPictureParts(*thePage, 1, 0, 1, &iBackwashShape, nil, nil, nil);
  449.     GXGetGraphicsError(&grErr);
  450.  
  451.  
  452. // Jump points for misc. error conditions follow.
  453.  
  454. GetFormatDims_Failed:
  455. CouldNotSetUpShape:
  456. TempNewHandle_Failed:
  457. DespoolResource_Failed:
  458. NotAddingBackwash:
  459. ForwardMessage_Failed:
  460.  
  461.     return grErr;
  462. }
  463.  
  464.  
  465. /*******************************************************************
  466.     BWCloseSpoolFile is our override for the GXCloseSpoolFile
  467.     message.  If the instance-global shape "iBackwashShape" is not nil, we
  468.     dispose of it.  Remember that we initialized it to nil in
  469.     BWInitialize, so the only way it can be non-nil is if we just
  470.     used it during despooling.
  471.     
  472. ********************************************************************/
  473.  
  474. OSErr BWCloseSpoolFile(gxSpoolFile theSpoolFile, long closeOptions)
  475. {
  476.     gxShape                iBackwashShape ;
  477.  
  478.     iBackwashShape = GetBackwashShape() ;
  479.  
  480. // Dispose of the shape and "nil out" our global, if necessary.
  481. // Then, forward the message so that the spool file is closed.
  482.  
  483.     if (iBackwashShape != nil)
  484.     {
  485.         GXDisposeShape(iBackwashShape);
  486.         iBackwashShape = nil;
  487.     }
  488.  
  489.     return Forward_GXCloseSpoolFile(theSpoolFile, closeOptions);
  490. }
  491.  
  492.  
  493. /*******************************************************************
  494.     SetUpPrintPanel sets up our print panel, adding a default
  495.     "Backwash settings" collection item to the job collection if
  496.     there isn't already one.  This collection item has the values
  497.     we'll use to set up our panel's controls.
  498.     
  499. ********************************************************************/
  500.  
  501. OSErr SetUpPrintPanel()
  502. {
  503.     OSErr                        err;
  504.     gxPanelSetupRecord            panelSetupRec;
  505.     BackwashCollection            backwashConfig;
  506.  
  507. // Get the job collection and try to find our backwash settings.
  508.  
  509.     err = GetJobCollectionItem(&backwashConfig,
  510.                                nil,
  511.                                kBackwashCollectionType,
  512.                                kBackwashSettingsID);
  513.  
  514.  
  515. // If our collection item (settings) don't exist yet, create a default
  516. // collection item and add that to the job collection.
  517.  
  518.  
  519.     if (err == collectionItemNotFoundErr)
  520.     {
  521.         FSSpec    dafaultFSSpec = { 0,0,"\pNone" } ;
  522.  
  523.         backwashConfig.intensity = kDefaultIntensity;
  524.         backwashConfig.addBackwash = kDontAddBackwash;
  525.         backwashConfig.useTextEditMode = false;
  526.         backwashConfig.haveFileInfo = false;
  527.         backwashConfig.fileInfo = dafaultFSSpec ;
  528.  
  529.         err = AddCollectionItem(GXGetJobCollection(GXGetJob()),
  530.                                 kBackwashCollectionType,
  531.                                 kBackwashSettingsID,
  532.                                 sizeof(BackwashCollection),
  533.                                 &backwashConfig);
  534.     }
  535.  
  536.     nrequire(err, StoreSettings_Failed);
  537.  
  538.  
  539. // Now do the actual panel set up.
  540.  
  541.     panelSetupRec.panelResId        = r_BackwashPanel;    // Which panel resource?
  542.     panelSetupRec.resourceRefNum    = GXGetMessageHandlerResFile();    // Where is it?
  543.     panelSetupRec.refCon            = 0;                // We don't use this.
  544.     panelSetupRec.panelKind            = gxExtensionPanel;    // This is an extension panel.
  545.     
  546.     err = GXSetupDialogPanel(&panelSetupRec);
  547.  
  548.  
  549. StoreSettings_Failed:
  550.  
  551.     return err;
  552. }
  553.  
  554.  
  555. /*******************************************************************
  556.     OpenBackwashPanel handles non-'xdtl' item initialization when
  557.     we open our panel.  Note that our items will be offset from
  558.     itemCount.  (So, if we want item #5 in our DITL, we pass
  559.     itemCount +5 to GetDItem.)
  560.     
  561.     The only non-'xdtl' item we have in our panel is the PICTfile
  562.     name.  We initialize that here.
  563.     
  564. ********************************************************************/
  565.  
  566. void OpenBackwashPanel(DialogPtr pDlg, short itemCount)
  567. {
  568.     BackwashCollection    backwashConfig;
  569.     Handle                hItem;
  570.     Rect                itemRect;
  571.     short                itemType;
  572.  
  573. // Initialize the current file name displayed, based on the
  574. // settings in our backwash collection item.
  575.  
  576.     GetJobCollectionItem(&backwashConfig,
  577.                          nil,
  578.                          kBackwashCollectionType,
  579.                          kBackwashSettingsID);
  580.  
  581.     GetDItem(pDlg, itemCount +d_FileNameItem, &itemType, &hItem, &itemRect);
  582.     SetIText(hItem, backwashConfig.fileInfo.name);
  583. }
  584.  
  585.  
  586. /*******************************************************************
  587.     AddBackwash is the routine we call at GXCreateSpoolFile time
  588.     to actually add the backwash to our spool file.  We pass in
  589.     info on where the PICT file is, the intensity the user
  590.     requested (as an integral percentage 0-100), and the spool
  591.     file reference.
  592.  
  593. ********************************************************************/
  594.  
  595. gxGraphicsError AddBackwash(FSSpecPtr fileInfo, short theIntensity, gxSpoolFile theSpoolFile)
  596. {
  597.     gxGraphicsError    grErr;
  598.     Rect            pictBounds, itemRect;
  599.     PicHandle        backwashPict;
  600.     Point            patStretchPoint = {1,1};        // this means "don't stretch."
  601.     Handle            textHdl, gxShapeHdl = nil;
  602.     gxShape            gxPictShape;
  603.     short            resAttribs, oldResFile, ourResFile, itemKind;
  604.     Str255            statMsg;
  605.     DialogPtr        theMessageDlog;
  606.     unsigned long    endTicks;
  607.     GrafPtr            oldPort;
  608.  
  609.     GetPort(&oldPort);
  610.     oldResFile = CurResFile();
  611.     ourResFile = GXGetMessageHandlerResFile();
  612.     UseResFile(ourResFile);
  613.  
  614.  
  615. // Load the dialog that we'll display status messages in.  Note that we
  616. // CANNOT use GXAlertTheUser or GXReportStatus in this routine, because
  617. // those only work when the Finder or PrinterShare GX are the active
  618. // applications (when we're spooling, the user's app is active.)  Load
  619. // our first status string, and show/update the dialog.
  620.  
  621.     theMessageDlog = GetNewDialog(r_BackwashStatus, nil, (WindowPtr) -1);
  622.     nrequire((grErr = (gxGraphicsError) ResError()), CouldNotLoadDialog);
  623.     GetDItem(theMessageDlog, d_StatusFieldItem, &itemKind, &textHdl, &itemRect);
  624.  
  625.     GetIndString(statMsg, r_BackwashStatus, s_StatusLoadPict);
  626.     nrequire((grErr = (gxGraphicsError) ResError()), CouldNotLoadString);
  627.     SetIText(textHdl, statMsg);
  628.     SetPort(theMessageDlog);
  629.     PositionWindow(theMessageDlog, true, n_dlogLevel);
  630.     DrawDialog(theMessageDlog);
  631.  
  632.  
  633. // Load the picture from disk.  If we can't load it, we display a message
  634. // saying so, and then exit the routine after 3 seconds.
  635.  
  636.     GXJobIdle();
  637.     backwashPict = LoadAPict(fileInfo);
  638.  
  639.     UseResFile(ourResFile);
  640.     nrequire(backwashPict, PictureLoaded);
  641.     GetIndString(statMsg, r_BackwashStatus, s_PictureNotLoaded);
  642.     nrequire((grErr = (gxGraphicsError) ResError()), CouldNotLoadString);
  643.     SetIText(textHdl, statMsg);
  644.     
  645.     SysBeep(0);
  646.     endTicks = TickCount() + (3 * 60);
  647.     
  648.     while (endTicks > TickCount())
  649.         GXJobIdle();
  650.  
  651.     require_action(backwashPict, CouldNotLoadPICT, grErr = nilHandleErr;);
  652.  
  653.  
  654. // Create a shape to store our converted PICT in, load and display another
  655. // status string, and then convert the PICT into a QuickDraw GX shape.
  656.  
  657. PictureLoaded:
  658.  
  659.     pictBounds = (*backwashPict)->picFrame;
  660.     gxPictShape = GXNewShape(gxPictureType);            // shape to store picture in.
  661.     nrequire_action(GXGetGraphicsError(&grErr), CouldNotCreateShape,
  662.                     KillPicture(backwashPict););
  663.     
  664.     UseResFile(ourResFile);
  665.     GetIndString(statMsg, r_BackwashStatus, s_ConvertPict);
  666.     nrequire((grErr = (gxGraphicsError) ResError()), CouldNotLoadString);
  667.     SetIText(textHdl, statMsg);
  668.     
  669.     GXJobIdle();
  670.  
  671.     GXConvertPICTToShape(backwashPict,                    /* this is the original.    */
  672.                          gxDefaultOptionsTranslation,    /* use default settings.    */
  673.                          &pictBounds,                    /* source/dest bounds.        */
  674.                          &pictBounds,
  675.                          patStretchPoint,                /* how to stretch (don't).    */
  676.                          gxPictShape,                    /* the destination.            */
  677.                          nil);                            /* we don't want details.    */
  678.     
  679.     KillPicture(backwashPict);                    // original is converted so kill it.
  680.  
  681.     nrequire_action(GXGetGraphicsError(&grErr), CouldNotConvertShape,
  682.                     GXDisposeShape(gxPictShape););
  683.  
  684.  
  685. // Load and display another status string, lighten the GX picture shape as we
  686. // need to, and flatten the shape into a handle.
  687.  
  688.     UseResFile(ourResFile);
  689.     GetIndString(statMsg, r_BackwashStatus, s_SetShapeIntensity);
  690.     nrequire((grErr = (gxGraphicsError) ResError()), CouldNotLoadString);
  691.     SetIText(textHdl, statMsg);
  692.     
  693.     GXJobIdle();
  694.  
  695.     UseResFile(ourResFile);
  696.     grErr = SetShapeIntensity(gxPictShape, theIntensity);
  697.     nrequire_action(grErr, CouldNotSetShapeIntensity, GXDisposeShape(gxPictShape););
  698.     gxShapeHdl = ShapeToHandle(gxPictShape);
  699.     grErr = (gxGraphicsError) MemError();
  700.     GXDisposeShape(gxPictShape);                // all done with this.
  701.     nrequire(grErr, CouldNotFlattenShape);
  702.  
  703.  
  704. // Load and display our last status string, and add the shape as a resource in
  705. // the spool file.  After adding it, update the resource so it's marked
  706. // "sysHeap."  We do this so that the resource won't be loaded into
  707. // PrinterShare GX's heap at despool time.  Since the data may be large, and
  708. // PrinterShare GX's heap is small, this will make things work better.
  709.  
  710.     GetIndString(statMsg, r_BackwashStatus, s_SpoolShapeResource);
  711.     nrequire((grErr = (gxGraphicsError) ResError()), CouldNotLoadString);
  712.     SetIText(textHdl, statMsg);
  713.     
  714.     GXJobIdle();
  715.  
  716.     grErr = Send_GXSpoolResource(theSpoolFile, gxShapeHdl, kBackwashCollectionType, r_BackwashPICTID);
  717.     nrequire_action(grErr, CouldNotAddShape, DisposHandle(gxShapeHdl););
  718.  
  719.     resAttribs = GetResAttrs(gxShapeHdl);
  720.     SetResAttrs(gxShapeHdl, resAttribs | resSysHeap);
  721.     ChangedResource(gxShapeHdl);
  722.     WriteResource(gxShapeHdl);
  723.  
  724.  
  725. // Finally release the resource (DON'T call DisposHandle on it!!), dispose of
  726. // our dialog, and return.
  727.  
  728.     ReleaseResource(gxShapeHdl);
  729.  
  730.  
  731. // Jump points for misc. error conditions follow.
  732.  
  733. CouldNotAddShape:
  734. CouldNotFlattenShape:
  735. CouldNotSetShapeIntensity:
  736. CouldNotConvertShape:
  737. CouldNotCreateShape:
  738. CouldNotLoadPICT:
  739. CouldNotLoadString:
  740.  
  741.     SetPort(oldPort);
  742.     DisposDialog(theMessageDlog);
  743.  
  744. CouldNotLoadDialog:
  745.  
  746.     UseResFile(oldResFile);
  747.     return grErr;
  748. }
  749.  
  750.  
  751. /*******************************************************************
  752.     RemoveWhiteRects is the routine which strips white rectangles
  753.     out of a page shape if we're in "TextEdit compatibility" mode.
  754.     Apps which print text by using TextEdit and calling TEUpdate
  755.     inadvertently add white rectangles behind the text they're
  756.     drawing.  This is because TEUpdate erases before it draws, and
  757.     this erasure is translated into a white rectangle by GX.  It's
  758.     as if you drew a white rectangle shape (to erase) and then
  759.     drew the text on top of that.  This is yet another reason why
  760.     DTS recommends that you not use TextEdit for printing.
  761.     
  762.     This routine calls itself recursively if a picture is passed.
  763.     The return value for each call indicates whether or not you
  764.     should remove the shape passed.  The original caller is
  765.     expected to pass a picture in, so it need not worry about the
  766.     boolean value returned.  (It should never be set, since a
  767.     picture ≠ a white rect.)
  768.  
  769. ********************************************************************/
  770.  
  771. Boolean RemoveWhiteRects(gxShape theShape)
  772. {
  773.     gxGraphicsError        grErr = noErr;
  774.     gxShapeType            typeOfShape;
  775.     gxShape                nextShape;
  776.  
  777. // Get the type of shape we were passed.  If it's a picture shape,
  778. // get the number of parts in the picture.
  779.  
  780.     require(theShape, ShapeIsNIL);
  781.     typeOfShape = GXGetShapeType(theShape);
  782.  
  783.     if (typeOfShape == gxPictureType)
  784.     {
  785.         long    idx, numParts;
  786.         
  787.         numParts = GXGetPicture(theShape, nil, nil, nil, nil);
  788.  
  789.  
  790. // Loop through each picture item.  If RemoveWhiteRects says it's a
  791. // white rectangle, remove it.  When we remove a picture item, we
  792. // must decrement both the picture item index and the number of
  793. // items in the picture.  That way, our loop still works.
  794.  
  795.         for (idx = 1; idx <= numParts; idx ++)
  796.         {
  797.             GXGetPictureParts(theShape, idx, 1, &nextShape, nil, nil, nil);
  798.  
  799.             if (RemoveWhiteRects(nextShape))
  800.             {
  801.                 GXSetPictureParts(theShape, idx, 1, 0, nil, nil, nil, nil);
  802.                 idx -= 1;
  803.                 numParts -= 1;
  804.             }
  805.         }
  806.     }
  807.     else
  808.  
  809. // We weren't passed a picture.  See if it's a white rectangle which is being
  810. // "source copied."  If so, return true-- this may be a TextEdit erasure.
  811.  
  812.         if (typeOfShape == gxRectangleType)
  813.         {
  814.             gxColor            shapeColor;
  815.             gxTransferMode    shapeTransfer;
  816.                     
  817.             GXGetShapeColor(theShape, &shapeColor);
  818.             GXGetShapeTransfer(theShape, &shapeTransfer);
  819.  
  820.             if ((shapeColor.element.rgb.red   == 0xFFFF) &&
  821.                 (shapeColor.element.rgb.green == 0xFFFF) &&
  822.                 (shapeColor.element.rgb.blue  == 0xFFFF) &&
  823.                 (shapeTransfer.component[0].mode == gxCopyMode))
  824.                 return true;
  825.         }
  826.  
  827. ShapeIsNIL:
  828.  
  829.     return false;
  830. }
  831.  
  832.  
  833. /*******************************************************************
  834.     SetShapeIntensity is a routine which lightens the shape passed
  835.     based on the percentage specified.  For example, an intensity
  836.     of 100 (100%) means that the shape should appear normal.  At
  837.     50, (50%) the shape will be half as dark, at 0 (0%) the shape
  838.     would simply fade to white.
  839.     
  840.     This routine calls itself recursively if a picture is passed.
  841.     For each item in the shape passed, (and all items contained in
  842.     sub-pictures), the shape's ink is set to blend mode, and the
  843.     item is blended by the percentage passed.  Since the background
  844.     during printing is white, this effectively fades a shape to
  845.     white.
  846.  
  847. ********************************************************************/
  848.  
  849. gxGraphicsError SetShapeIntensity(gxShape theShape, short theIntensity)
  850. {
  851.     gxGraphicsError        grErr = noErr;
  852.     gxShapeType            typeOfShape;
  853.     gxShape                nextShape;
  854.  
  855. // Get the type of shape we were passed.  If it's a picture shape,
  856. // get the number of parts in the picture.
  857.  
  858.     require(theShape, ShapeIsNIL);
  859.     require((theIntensity != 100), UsingFullIntensity);
  860.     typeOfShape = GXGetShapeType(theShape);
  861.  
  862.     if (typeOfShape == gxPictureType)
  863.     {
  864.         long    idx, numParts;
  865.         
  866.         numParts = GXGetPicture(theShape, nil, nil, nil, nil);
  867.  
  868.  
  869. // For each item in the picture, set the shape's intensity.
  870.  
  871.         for (idx = 1; !grErr && (idx <= numParts); idx ++)
  872.         {
  873.             GXGetPictureParts(theShape, idx, 1, &nextShape, nil, nil, nil);
  874.             grErr = SetShapeIntensity(nextShape, theIntensity);
  875.         }
  876.     }
  877.     else
  878.  
  879. // We weren't passed a picture.  Get the ink of this shape and set its
  880. // first (and only) transfer mode to "blend", maximize and minimize all
  881. // the bounding values, and set the transfer's operand to the percentage
  882. // passed in (scaled so that it's between 0 and 0xFFFF).  Finally, set
  883. // the shape's transfer to this new value.
  884.  
  885.     {
  886.         gxTransferMode    shapeTransfer;
  887.         
  888.         GXGetShapeTransfer(theShape, &shapeTransfer);
  889.  
  890.         shapeTransfer.flags = gxSingleComponentTransfer;
  891.  
  892.         shapeTransfer.component[0].mode = gxBlendMode;
  893.         shapeTransfer.component[1].mode =
  894.         shapeTransfer.component[2].mode =
  895.         shapeTransfer.component[3].mode = 0;
  896.  
  897.         shapeTransfer.component[0].flags = 0;
  898.         shapeTransfer.component[0].sourceMinimum = 0;
  899.         shapeTransfer.component[0].sourceMaximum = 0xFFFF;
  900.         shapeTransfer.component[0].deviceMinimum = 0;
  901.         shapeTransfer.component[0].deviceMaximum = 0xFFFF;
  902.         shapeTransfer.component[0].clampMinimum = 0;
  903.         shapeTransfer.component[0].clampMaximum = 0xFFFF;
  904.         shapeTransfer.component[0].operand = (0xFFFF * theIntensity)/100;
  905.  
  906.         GXSetShapeTransfer(theShape, &shapeTransfer);
  907.         GXGetGraphicsError(&grErr);
  908.     }
  909.  
  910. ShapeIsNIL:
  911. UsingFullIntensity:
  912.  
  913.     return grErr;
  914. }
  915.  
  916.  
  917. /*******************************************************************
  918.     LoadAPict is a utility routine that just loads a PICT.  We
  919.     create a MultiFinder temporary memory handle and return the
  920.     pict in that.  This way, we don't impose on the application's
  921.     memory space.
  922.  
  923. ********************************************************************/
  924.  
  925. PicHandle LoadAPict(FSSpecPtr opFSSpec)
  926. {
  927.     OSErr            err;
  928.     long            count;
  929.     short            pictRefNum;
  930.     PicHandle        backwashPict = nil;
  931.  
  932. // Open the file.
  933.  
  934.     err = FSpOpenDF(opFSSpec, fsCurPerm, &pictRefNum);
  935.     nrequire(err, CouldNotOpenFile);
  936.  
  937. // Get the length of the file minus the 512 byte picture header.
  938.  
  939.     err = GetEOF(pictRefNum, &count);
  940.     nrequire(err, CouldNotGetEOF);
  941.     count -= 512;
  942.  
  943.  
  944. // Create a handle for our PICT and read the data (Except the header)
  945. // from the file into the handle.
  946.  
  947.     err = SetFPos(pictRefNum, fsFromStart, 512);
  948.     nrequire(err, CouldNotSetFilePos);
  949.  
  950.     backwashPict = (PicHandle) TempNewHandle(count, &err);
  951.     nrequire(err, CouldNotCreateHandle);
  952.  
  953.     HLock((Handle) backwashPict);
  954.     err = FSRead(pictRefNum, &count, *backwashPict);
  955.     HUnlock((Handle) backwashPict);
  956.  
  957.  
  958. // If we had an error, kill the picture.  Otherwise, just close the file.
  959.  
  960.     if(err)
  961.     {
  962.         KillPicture(backwashPict);
  963.         backwashPict = nil;
  964.     }
  965.  
  966. CouldNotCreateHandle:
  967. CouldNotSetFilePos:
  968. CouldNotGetEOF:
  969.  
  970.     FSClose(pictRefNum);
  971.  
  972. CouldNotOpenFile:
  973.  
  974.     return backwashPict;
  975. }
  976.  
  977.  
  978. /*******************************************************************
  979.     GetJobCollectionItem is a generic routine that retrieves a
  980.     collection item from the job collection.
  981.     
  982. ********************************************************************/
  983.  
  984. OSErr GetJobCollectionItem(void *collectItem, long *collectSize,
  985.                            OSType collectType, short collectID)
  986. {
  987.     return GetCollectionItem(GXGetJobCollection(GXGetJob()),
  988.                              collectType,
  989.                              collectID,
  990.                              collectSize,
  991.                              collectItem);
  992. }
  993.  
  994.  
  995. /*******************************************************************
  996.     HandleSpoolProc is a spooling routine for GXFlattenShape and
  997.     GXUnflattenShape (called from ShapeToHandle and HandleToShape).
  998.     This routine (which was taken from "storage library.c"),
  999.     flattens a gxShape into a handle.
  1000.  
  1001. ********************************************************************/
  1002.  
  1003. gxGraphicsError HandleSpoolProc(gxSpoolCommand command,  UserSpool *block)
  1004. {
  1005.     OSErr    err = noErr;
  1006.  
  1007.     switch (command)
  1008.     {
  1009.         case gxOpenReadSpool:    // About to unflatten-- reset the handle size and offset.
  1010.  
  1011.             block->size = 0;
  1012.             block->position = 0;
  1013.             break;
  1014.  
  1015.         case gxOpenWriteSpool:    // About to flatten-- create a handle to flatten into,
  1016.                                 // using our initial handle size.
  1017.         
  1018.             block->data = TempNewHandle(kAllocationIncrement, &err);
  1019.             block->size = kAllocationIncrement;
  1020.             block->position = 0;
  1021.             break;
  1022.       
  1023.         case gxReadSpool:        // Some data has been unflattened.  Move the data to
  1024.                                 // our buffer and increase the buffer offset.
  1025.  
  1026.             BlockMove((*(char **) block->data + block->position), block->spool.buffer, block->spool.count);
  1027.             block->position += block->spool.count;
  1028.             break;
  1029.         
  1030.         case gxWriteSpool:        // Some data has been flattened.
  1031.  
  1032.             {
  1033.                 register long oldPosition;
  1034.                 
  1035.                                 // If we need to expand the buffer to hold the
  1036.                                 // new data, do so.  We always make sure there
  1037.                                 // is enough room for one "bufferSize" past the
  1038.                                 // current point.  Move the data to our buffer
  1039.                                 // and increase the buffer offset.
  1040.                 
  1041.                 oldPosition = block->position;
  1042.                 block->position += block->spool.count;
  1043.                 
  1044.                 if (block->position + block->spool.bufferSize > block->size)      
  1045.                 {
  1046.                     block->size += block->spool.bufferSize;
  1047.                     HUnlock(block->data);
  1048.                     SetHandleSize(block->data, block->size);
  1049.                     err = MemError();
  1050.                     HLock(block->data);
  1051.                 }
  1052.                 BlockMove(block->spool.buffer, (*(char **) block->data + oldPosition), block->spool.count);
  1053.             }
  1054.             break;
  1055.       
  1056.         case gxCloseSpool:        // Finishing up.  Size the buffer to the data's
  1057.                                 // final size.
  1058.  
  1059.             SetHandleSize(block->data, block->position);
  1060.             err = MemError();
  1061.             break;
  1062.     }
  1063.  
  1064.     return (gxGraphicsError) err;
  1065. }
  1066.  
  1067.  
  1068. /*******************************************************************
  1069.     ShapeToHandle flattens the passed shape into a handle, which
  1070.     it returns.  This routine was taken from the "storage library.c"
  1071.     file.  It simply calls GXFlattenShape with a custom
  1072.     gxSpoolProcedure which flattens the shape into a handle.
  1073.  
  1074. ********************************************************************/
  1075.  
  1076. Handle ShapeToHandle(gxShape source)
  1077. {
  1078.     UserSpool block;
  1079.     
  1080.     block.spool.spoolProcedure = (long (*)(gxSpoolCommand, struct gxSpoolBlock *)) HandleSpoolProc;
  1081.     block.spool.buffer = nil;
  1082.     block.spool.bufferSize = 0;
  1083.     GXFlattenShape(source, gxFontListFlatten | gxFontGlyphsFlatten, &block.spool);
  1084.     return block.data;
  1085. }
  1086.  
  1087.  
  1088. /*******************************************************************
  1089.     HandleToShape unflattens the passed handle into its original
  1090.     gxShape, which it returns.  This routine was taken from the
  1091.     "storage library.c" file.  It simply calls GXUnflattenShape
  1092.     with a custom gxSpoolProcedure which unflattens the shape from
  1093.     a handle.
  1094.  
  1095. ********************************************************************/
  1096.  
  1097. gxShape HandleToShape(Handle source, long count, const gxViewPort portList[])
  1098. {
  1099.     UserSpool block;
  1100.     
  1101.     block.spool.spoolProcedure = (long (*)(gxSpoolCommand, struct gxSpoolBlock *)) HandleSpoolProc;
  1102.     block.spool.buffer = nil;
  1103.     block.spool.bufferSize = 0;
  1104.     block.data = source;
  1105.     return GXUnflattenShape(&block.spool, count, portList);
  1106. }
  1107.  
  1108.  
  1109. /*******************************************************************
  1110.     PositionWindow - This routine positions a window on the
  1111.     screen, centered horizontally and positioned vertPercent
  1112.     down the current screen.  If showIt is true, we make the
  1113.     window visible, otherwise we hide it.
  1114.  
  1115. ********************************************************************/
  1116.  
  1117. void PositionWindow(WindowPtr windPtr, Boolean showIt, float vertPercent)
  1118. {
  1119.     Rect        devRect;
  1120.     short        amtFromTop, windWidth, deviceWidth;
  1121.     GDHandle    curGDH;
  1122.  
  1123. // Get the current GDevice and use its bounds to calculate where we
  1124. // should move the window to.
  1125.  
  1126.     if (windPtr != nil)
  1127.     {
  1128.         curGDH = GetGDevice();
  1129.         devRect = (*curGDH)->gdRect;
  1130.  
  1131.         amtFromTop = (devRect.bottom - devRect.top +1) * vertPercent;
  1132.         windWidth = windPtr->portRect.right - windPtr->portRect.left +1;
  1133.         deviceWidth = devRect.right - devRect.left +1;
  1134.  
  1135.  
  1136. // Hide the window, move it, and (if we're supposed to) show it again.
  1137.  
  1138.         ShowHide(windPtr, false);
  1139.         MoveWindow(windPtr, devRect.left + (deviceWidth /2 - windWidth /2), devRect.top + amtFromTop, true);
  1140.         if (showIt) ShowHide(windPtr, true);
  1141.     }
  1142. }
  1143.